using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DotLiquid;
using Nancy.Extensions;
using Wolfpack.Periscope.Core.Extensions;
using Wolfpack.Periscope.Core.Infrastructure;
using Wolfpack.Periscope.Core.Interfaces;
using Wolfpack.Periscope.Core.Interfaces.Entities;

namespace Wolfpack.Periscope.Core.Widgets
{
    public class TemplatedWidgetRenderer : IWidgetRenderer
    {
        private readonly IWidgetScriptCache _cache;

        public TemplatedWidgetRenderer()
        {
            Id = GetType().Name;
            Description = "Template (DotLiquid) based widget renderer";
            DemoUrl = "http://periscope.codeplex.com/documentation/widgets/templatewidgetrenderer";
            SampleMessage = "Check individual templates for example data messages";
            _cache = new DefaultWidgetScriptCache();
        }

        public string Id { get; private set; }        
        public string Description { get; private set; }
        public string DemoUrl { get; private set; }
        public string SampleMessage { get; private set; }

        public RenderedWidget Render(WidgetInstance instance)
        {
            return new RenderedWidget
            {
                RendererId = instance.RendererId,
                Id = instance.Id,
                Column = instance.Column,
                Description = instance.Description,
                Height = instance.Height,
                Row = instance.Row,
                Title = instance.Title,
                Width = instance.Width,

                Markup = GetMarkup(instance),
                SampleMessage = GetSampleMessage(instance),
                Scripts = GetScripts(instance),
                StyleSheets = GetStyleSheets(instance)
            };
        }

        private string GetMarkup(WidgetInstance wi)
        {
            return RenderTemplate(wi, wi.MarkupTemplate, "html");
        }
        private string GetSampleMessage(WidgetInstance wi)
        {
            return RenderTemplate(wi, wi.SampleTemplate, "txt");
        }

        private IEnumerable<Property> GetScripts(WidgetInstance instance)
        {
            var script = RenderTemplate(instance, instance.ScriptTemplate, "js");
            var cachedItem = _cache.WriteScript(instance, script);

            // note order of includes is important!
            var scripts = new List<Property>(GetScriptIncludes(instance));
            return scripts.Concat(instance.ScriptIncludes ?? new Property[0].ToList())
                .Concat(cachedItem)
                .DistinctBy(x => x.Name);
        }

        private IEnumerable<Property> GetStyleSheets(WidgetInstance instance)
        {
            var css = RenderTemplate(instance, instance.CssTemplate, "css");
            var cachedItem = _cache.WriteCss(instance, css);

            var stylesheets = new List<Property>(cachedItem);
            return stylesheets.Concat(instance.StyleSheetIncludes ?? new Property[0].ToList())
                .DistinctBy(x => x.Name);
        }

        public virtual List<Property> GetScriptIncludes(WidgetInstance instance)
        {
            var folder = (string.IsNullOrWhiteSpace(instance.TemplatesFolder)
                ? string.Format(@"widgets\templates\{0}", instance.ScriptTemplate)
                : instance.TemplatesFolder)
                .Trim('\\');

            var filename = Path.Combine(folder, "includes.txt");

            if (!File.Exists(filename))
                return new List<Property>();

            return File.ReadAllLines(filename)
                .Where(x => !string.IsNullOrWhiteSpace(x))
                .Select(x =>
                {
                    var delimiterIndex = x.IndexOf("=", StringComparison.Ordinal);

                    return new Property
                    {
                        Name = x.Substring(0, delimiterIndex),
                        Value = x.Substring(delimiterIndex + 1)
                    };
                })
                .ToList();
        }

        protected virtual string RenderTemplate(WidgetInstance instance, string templateName, string fileExt)
        {
            var folder = (string.IsNullOrWhiteSpace(instance.TemplatesFolder)
                ? string.Format(@"widgets\templates\{0}", templateName)
                : instance.TemplatesFolder).Trim('\\');

            var filename = string.Format(@"{0}\{1}.{2}", folder, templateName, fileExt);

            if (!File.Exists(filename))
                return null;

            using (var sr = new StreamReader(filename))
            {
                var templateContents = sr.ReadToEnd();
                var template = Template.Parse(templateContents);

                var data = new Dictionary<string, string>(instance.TemplateData);

                return template.Render(Hash.FromDictionary(data.AddFromAnonymous(new {id = instance.Id})
                    .ToDictionary(x => x.Key, x => (object) x.Value)));
            }
        }
    }
}